//
// Copyright (c) 2011 Alex Nesterenko
//

#include "nane/RenderSystem/ogl/OGLXWindowContext.h"
#include "nane/RenderSystem/ogl/OGLAPI.h"
#include "nane/core/assert.h"
#include "nane/core/log.h"
#include "nane/core/ISubSystem.h"

#define GLX_USE_GL		1
#define GLX_BUFFER_SIZE		2
#define GLX_LEVEL		3
#define GLX_RGBA		4
#define GLX_DOUBLEBUFFER	5
#define GLX_STEREO		6
#define GLX_AUX_BUFFERS		7
#define GLX_RED_SIZE		8
#define GLX_GREEN_SIZE		9
#define GLX_BLUE_SIZE		10
#define GLX_ALPHA_SIZE		11
#define GLX_DEPTH_SIZE		12
#define GLX_STENCIL_SIZE	13
#define GLX_ACCUM_RED_SIZE	14
#define GLX_ACCUM_GREEN_SIZE	15
#define GLX_ACCUM_BLUE_SIZE	16
#define GLX_ACCUM_ALPHA_SIZE	17

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    template<>
    void createSubSystem<OGLWindowContext>( OGLWindowContext** _system )
    {
        *_system = new OGLXWindowContext();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    template<>
    void releaseSubSystem<OGLWindowContext>( OGLWindowContext* _system )
    {
        delete static_cast<OGLXWindowContext*>(_system);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    OGLXWindowContext::OGLXWindowContext()
        : m_windowHandle( NULL )
        , m_display( NULL )
        , m_hOpenGL( NULL )
        , m_hGLContext( NULL )
        , m_hSubWindow( 0 )
        , m_pGLXChooseVisual( NULL )
        , m_pGLXCreateContext( NULL )
        , m_pGLXMakeCurrent( NULL )
        , m_pGLXDestroyContext( NULL )
        , m_pGLXSwapBuffers( NULL )
    {

    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    OGLXWindowContext::~OGLXWindowContext()
    {

    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool OGLXWindowContext::initialize( IWindow::OSHandle _windowHandle )
    {
        if( _windowHandle == NULL )
        {
            LOG_ERROR("Window handle must be valid");
            return false;
        }
        m_windowHandle = _windowHandle;
        m_display = XOpenDisplay(NULL);
        if( m_display == NULL )
        {
            LOG_ERROR("Failed to open X Display");
            return false;
        }

        m_hOpenGL = DynamicModule::loadModule("libGL.so.1");
        m_pGLXChooseVisual = reinterpret_cast<PFUNCGLXCHOOSEVISUAL>(DynamicModule::getFunction(m_hOpenGL, "glXChooseVisual"));
        m_pGLXCreateContext = reinterpret_cast<PFUNCGLXCREATECONTEXT>(DynamicModule::getFunction(m_hOpenGL, "glXCreateContext"));
        m_pGLXMakeCurrent = reinterpret_cast<PFUNCGLXMAKECURRENT>(DynamicModule::getFunction(m_hOpenGL, "glXMakeCurrent"));
        m_pGLXDestroyContext = reinterpret_cast<PFUNCGLXDESTROYCONTEXT>(DynamicModule::getFunction(m_hOpenGL, "glXDestroyContext"));
        m_pGLXSwapBuffers = reinterpret_cast<PFUNCGLXSWAPBUFFERS>(DynamicModule::getFunction(m_hOpenGL, "glXSwapBuffers"));
        m_pGLXQueryExtension = reinterpret_cast<PFUNCGLXQUERYEXTENSION>(DynamicModule::getFunction(m_hOpenGL, "glXQueryExtension"));
        if( m_hOpenGL == NULL
            || m_pGLXChooseVisual == NULL
            || m_pGLXCreateContext == NULL
            || m_pGLXMakeCurrent == NULL
            || m_pGLXDestroyContext == NULL
            || m_pGLXSwapBuffers == NULL
            || m_pGLXQueryExtension == NULL )
        {
            LOG_ERROR("Failed to load OGL X API");
            return false;
        }
        
        int dummy = 0;
        if( m_pGLXQueryExtension(m_display, &dummy, &dummy) == False )
        {
            LOG_ERROR("OpenGL X extension not supported");
            return false;
        }
        //int attrList[] = { GLX_USE_GL, /*GLX_DEPTH_SIZE, 16, GLX_RGBA,*/ GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, 0 };
        int attrList[] = { GLX_RGBA,
		    GLX_RED_SIZE, 1,
		    GLX_GREEN_SIZE, 1,
		    GLX_BLUE_SIZE, 1,
		    GLX_DOUBLEBUFFER,
		    GLX_DEPTH_SIZE, 1,
		    None };
        XVisualInfo* vinfo = m_pGLXChooseVisual(m_display, DefaultScreen(m_display), attrList);
        if( vinfo == NULL )
        {
            LOG_ERROR("Failed to choose X visual");
            return false;
        }
        Window hWindow = reinterpret_cast<Window>(m_windowHandle);
        XWindowAttributes windowAttrs;
        XGetWindowAttributes(m_display, hWindow, &windowAttrs);
        XSetWindowAttributes setAttrs;
        setAttrs.background_pixel = 0;
        setAttrs.border_pixel = 0;
        setAttrs.colormap = XCreateColormap(m_display, RootWindow(m_display, DefaultScreen(m_display)), vinfo->visual, AllocNone);
        //setAttrs.event_mask = StructureNotifyMask | ExposureMask;
        unsigned long mask = CWBackPixel | CWBorderPixel | CWColormap;// | CWEventMask;
        m_hSubWindow = XCreateWindow(m_display, hWindow, 0, 0, windowAttrs.width, windowAttrs.height, 0, vinfo->depth, InputOutput, vinfo->visual, mask, &setAttrs);
        //XChangeWindowAttributes(m_display, hWindow, mask, &attr);
        /* set hints and properties */
//        {
//          XSizeHints sizehints;
//          sizehints.x = x;
//          sizehints.y = y;
//          sizehints.width  = width;
//          sizehints.height = height;
//          sizehints.flags = USSize | USPosition;
//          XSetNormalHints(dpy, win, &sizehints);
//          XSetStandardProperties(dpy, win, name, name,
//                                  None, (char **)NULL, 0, &sizehints);
//        }
        m_hGLContext = m_pGLXCreateContext(m_display, vinfo, NULL, True);
        XFree(vinfo);
        if( m_hGLContext == NULL )
        {
            LOG_ERROR("Failed to create GL X Context");
            return false;
        }
        XMapWindow(m_display, m_hSubWindow);
        if( m_pGLXMakeCurrent(m_display, m_hSubWindow, m_hGLContext) == False )
        {
            LOG_ERROR("Failed to setup GL X Context");
            return false;
        }

        if( InitializeOpenGLAPI("libGL.so.1") == false )
        {
            LOG_ERROR("Failed to load OGL API");
            return false;
        }

        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void OGLXWindowContext::shutdown()
    {
        ReleaseOpenGLAPI();
        if( m_hGLContext != NULL )
        {
            //m_pGLXMakeCurrent(m_display, m_hSubWindow, NULL);
            m_pGLXDestroyContext(m_display, m_hGLContext);
            m_hGLContext = NULL;
        }
        if( m_hSubWindow != 0 )
        {
            XDestroyWindow(m_display, m_hSubWindow);
            m_hSubWindow = 0;
        }
        DynamicModule::unloadModule(m_hOpenGL);
        if( m_display != NULL )
        {
            //XCloseDisplay(m_display);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void OGLXWindowContext::swapBuffers()
    {
        m_pGLXSwapBuffers(m_display, m_hSubWindow);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
